﻿using DeHeng_Mes.Constant;
using DeHeng_Mes.Dao;
using DeHeng_Mes.Dao.PlcDao;
using DeHeng_Mes.Model;
using DeHeng_Mes.Model.Boss;
using DeHeng_Mes.Model.ConfigModel;
using DeHeng_Mes.Model.OrderSingleton;
using DeHeng_Mes.Model.PlcModel;
using DeHeng_Mes.Model.PlcModel.door_01;
using DeHeng_Mes.Model.Singleton;
using DeHeng_Mes.Model.Table;
using DeHeng_Mes.Service;
using DeHeng_Mes.Utils;
using NPOI.HSSF.Record;
using NPOI.SS.Formula.Eval;
using NPOI.SS.Formula.Functions;
using S7.Net;
using SqlSugar;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using MappingTable = DeHeng_Mes.Utils.MappingTable;

namespace DeHeng_Mes.Thread
{
    public class LinePlcToMes
    {
        #region 全局成员
        OrderDao orderDao = new OrderDao();
        PlcDao plcDao = new PlcDao();
        DeviceDao deviceDao = new DeviceDao();
        List<PlcOrderConfigModel> readPlcList = PlcOrderConfigModelList.ReadPlcList;
        spotCheakService spotCheakService = new spotCheakService();
        #endregion

        /// <summary>
        /// 处理读取完成的信号，将追溯码与订单、节拍等信息绑定
        /// </summary>
        /// <param name="types">数据类型字典</param>
        /// <param name="orderModel">工单模型</param>
        public void ProcessOkStatus(PlcOrderConfigModel orderModel)
        {
            try
            {
                spotCheakService spotCheakService = new spotCheakService();
                // 初始化状态字段
                string stationStatus = $"{orderModel.PlcName}-STATUS";

                if (orderModel.PlcName.Equals("P5"))
                {

                }

                // 获取该Plc工位状态数组信息
                Tuple<int, int> byteTuple = MappingTable.GetBytesOff(stationStatus);

                Tuple<int, int> returnByteTuple = MappingTable.GetReturnBytesOff(stationStatus);

                // 读取状态数组数据
                byte[] bytes = orderModel.plc.ReadBytes(DataType.DataBlock, byteTuple.Item1, SystemConstant.ALL_STARTOFF, byteTuple.Item2);

                byte[] returnBytes = orderModel.plc.ReadBytes(DataType.DataBlock, returnByteTuple.Item1, SystemConstant.ALL_STARTOFF, returnByteTuple.Item2);

                // 获取状态字段对应偏移量
                Dictionary<string, Tuple<int, int>> fieldOffs = MappingTable.GetDicByName(stationStatus);

                foreach (var fieldOff in fieldOffs)
                {
                    // 循环调用方法读取值
                    string byteData = bytes[fieldOff.Value.Item1].ToString();

                    // 获取对应工位的已读信号
                    int off = MappingTable.GetReadOff(fieldOff.Key);

                    if (off == 1000)
                    {
                        continue;
                    }

                    byte result = 2;
                    byte returnResult = 0;

                    // 信号复位
                    if (!byteData.Equals("3") && returnBytes[off].ToString().Equals("2"))
                        // 写入对应的已读信号
                        orderModel.plc.Write(DataType.DataBlock, 5001, off, returnResult);

                    // 判断状态是否是完成状态
                    if (byteData.Equals("3"))
                    {
                        /*// 判断是否是后防的物料
                        OrderSingletonModel orderSingletonModel = OrderSingleton.Instance.Where(x => x.PlcName.Equals(orderModel.PlcName)).FirstOrDefault();

                        if (orderSingletonModel != null && MappingTable.GetP3P4Material(orderSingletonModel.MaterialId))
                        {
                            // 实时获取产量
                            byte[] orderBytes = orderModel.plc.ReadBytes(DataType.DataBlock, 6003, SystemConstant.ALL_STARTOFF, 258);

                            int num = S7.Net.Types.Int.FromByteArray(orderBytes.Skip(256).Take(2).ToArray());

                            OrderSingleton.AddNumNow(orderSingletonModel.OrderId, orderSingletonModel.PlcName, num);
                        }*/

                        // 写入对应的已读信号
                        orderModel.plc.Write(DataType.DataBlock, 5001, off, result);

                        SaveStationData(fieldOff.Key, orderModel); // 完成，保存数据
                    }

                    if (byteData.Equals("5"))
                    {
                        Tuple<int, int> checkData = MappingTable.GetBytesOff(orderModel.PlcName + "-CHECK");

                        byte[] chcakDataBytes = orderModel.plc.ReadBytes(DataType.DataBlock, checkData.Item1, SystemConstant.ALL_STARTOFF, checkData.Item2);

                        Dictionary<string, int> dic = new Dictionary<string, int>()
                        {
                        {"ST30A",0 },
                        {"ST30B",64 },
                        };
                        int index = dic[fieldOff.Key];

                        string cheakCode = S7.Net.Types.String.FromByteArray(chcakDataBytes.Skip(index).Take(64).ToArray()).Trim();

                        spotCheakService.StationSpotChackSave(orderModel, cheakCode,fieldOff.Key);

                        orderModel.plc.Write(DataType.DataBlock, 5001, off, 2);
                    }
                }
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError($"{orderModel.PlcName}处理读取完成的信号，将追溯码与订单、节拍等信息绑定失败，原因是：" + ex.Message);
            }
        }

        /// <summary>
        /// 保存工位相关数据
        /// </summary>
        /// <param name="station"></param>
        private void SaveStationData(string station, PlcOrderConfigModel orderModel)
        {
            try
            {
                // 获取当前工位的数据偏移量
                Dictionary<string, Tuple<int, int>> stationDataOff = MappingTable.GetDicByName(station);

                // 获取工位对应的数据块
                string operation = MappingTable.GetOperationNo(orderModel.PlcName, station);

                // 获取保存DB信息的二元组
                Tuple<int, int> dbTuple = MappingTable.GetBytesOff($"{orderModel.PlcName}-{operation}");

                // 读取数组信息
                byte[] bytes = orderModel.plc.ReadBytes(DataType.DataBlock, dbTuple.Item1, SystemConstant.ALL_STARTOFF, dbTuple.Item2);

                // 获取工站过程数据
                StationData data = PlcDataUtil.FromBytesToClass(bytes, stationDataOff, station);

                if (data != null && data.Code.Count > 0)
                {
                    // 初始化精确追溯码的集合
                    List<string> traceCodes = new List<string>();

                    // 初始化批次追溯码的集合
                    //List<string> traceBatchCodes = new List<string>();

                    // 初始化精确追溯码的主键集合
                    List<int> traceIds = new List<int>();

                    // 初始化弧焊主键集合
                    List<string> weldIds = new List<string>();

                    // 分类批次码与追溯码
                    foreach (var code in data.Code)
                    {
                        if (!string.IsNullOrEmpty(code))
                        {
                            traceCodes.Add(code);
                        }
                    }

                    // 存储精确追溯码
                    if (traceCodes.Count > 0)
                    {
                        foreach (var traceCode in traceCodes)
                        {
                            int traceId = orderDao.SaveCode(traceCode, orderModel);

                            if (traceId > 0 && !traceIds.Contains(traceId))
                            {
                                // 存储精追码表数据
                                traceIds.Add(traceId);

                                // 绑定精追码之间的关联关系
                                orderDao.BandingCodeAndCode(traceId, station);
                            }
                        }
                    }

                    // 存储工位过点数据
                    if (!orderDao.ProcessDataIsSave(data.Station, data.StartTime))
                    {
                        int processId = orderDao.ProcessDataSave(data, operation, station);

                        // 绑定过点数据与追溯码
                        foreach (var traceId in traceIds)
                        {
                            // 绑定精追码与批次码关联关系
                            orderDao.BandingProcessAndCode(traceId, processId, station);
                        }
                    }

                    if (station.Equals(SystemConstant.ST05B))
                        return;

                    int endOff = 0;

                    if (station.Contains("10") || station.Contains("20"))
                    {
                        endOff = 400;
                    }
                    else
                    {
                        endOff = 640;
                    }

                    // 读取Job数组信息
                    byte[] jobBytes = orderModel.plc.ReadBytes(DataType.DataBlock, 5002, SystemConstant.ALL_STARTOFF, endOff);

                    // 获取工艺数据
                    List<TraceWeld> weldDatas = GetWeldDataList(bytes, station, jobBytes);

                    // 插入弧焊数据
                    if (weldDatas != null && weldDatas.Count > 0)
                    {
                        foreach (TraceWeld traceWeld in weldDatas)
                        {
                            // 保存弧焊数据
                            int id = orderDao.WeldDataIsSave(traceWeld);

                            weldIds.Add(id.ToString());
                        }
                    }

                    // 将弧焊数据主键集合转换成带，的字符串
                    string weldIdsStr = string.Join(",", weldIds);

                    foreach (var traceId in traceIds)
                    {
                        // 存储二维码、过点数据、弧焊数据关联数据
                        if (!orderDao.CodeAndProcessAndWeldIsSave(traceId, weldIdsStr, "WELD"))
                            orderDao.DataSave(traceId, weldIdsStr, "WELD");
                    }

                    foreach (string code in data.Code)
                    {
                        //推送拉铆工艺数据
                        _ = Task.Run(() => { ViewService.InsertLineMesData(code, station, weldDatas); });
                    }
                }
                else
                {
                    Tools.ProduceLogTextError($"{orderModel.PlcName}处理{station}工位过程数据时读取到空值，请检查！");
                }
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError($"{orderModel.PlcName}处理{station}工位过程数据时发生异常，异常信息为{ex.Message}[Station]");
            }
        }

        /// <summary>
        /// 获取工艺数据
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        private List<TraceWeld> GetWeldDataList(byte[] bytes, string station, byte[] jobBytes)
        {
            // 初始化集合
            List<TraceWeld> weldDatas = new List<TraceWeld>();

            Dictionary<string, int> dicOff = MappingTable.GetWeldOff(station);

            if (dicOff != null)
            {
                for (int i = 0; i < dicOff["Off"]; i++)
                {
                    string robot = i > ((dicOff["Off"] / 2) - 1) ? "R2" : "R1";

                    string seamNo = MappingTable.GetRealSeamNo(S7.Net.Types.Int.FromByteArray(bytes.Skip(dicOff["SeamNo"] + 1618 * i).Take(2).ToArray()).ToString(), station, robot);

                    TraceWeld traceWeld = new TraceWeld();

                    traceWeld.SeamNo = seamNo;
                    traceWeld.StartTime = S7.Net.Types.DateTime.FromByteArray(bytes.Skip(dicOff["StartTime"] + 1618 * i).Take(8).ToArray()).ToString();
                    traceWeld.EndTime = S7.Net.Types.DateTime.FromByteArray(bytes.Skip(dicOff["EndTime"] + 1618 * i).Take(8).ToArray()).ToString();
                    traceWeld.RobotNo = robot;
                    traceWeld.Station = station;

                    string[] weldI = new string[100];
                    string[] weldU = new string[100];
                    string[] wireSpeed = new string[100];
                    string[] gasFlow = new string[100];
                    string[] job = new string[10];

                    for (int j = 0; j < 100; j++)
                    {
                        weldI[j] = S7.Net.Types.Real.FromByteArray(bytes.Skip(dicOff["WeldI"] + 1618 * i + j * 4).Take(4).ToArray()).ToString();
                        weldU[j] = S7.Net.Types.Real.FromByteArray(bytes.Skip(dicOff["WeldU"] + 1618 * i + j * 4).Take(4).ToArray()).ToString();
                        wireSpeed[j] = S7.Net.Types.Real.FromByteArray(bytes.Skip(dicOff["WireSpeed"] + 1618 * i + j * 4).Take(4).ToArray()).ToString();
                        gasFlow[j] = S7.Net.Types.Real.FromByteArray(bytes.Skip(dicOff["GasFlow"] + 1618 * i + j * 4).Take(4).ToArray()).ToString();

                        if (j < 10)
                        {
                            job[j] = S7.Net.Types.Int.FromByteArray(jobBytes.Skip(dicOff["Job"] + 20 * i + j * 2).Take(2).ToArray()).ToString();
                        }
                    }

                    traceWeld.WeldI = string.Join(",", weldI);
                    traceWeld.WeldU = string.Join(",", weldU);
                    traceWeld.WireSpeed = string.Join(",", wireSpeed);
                    traceWeld.GasFlow = string.Join(",", gasFlow);
                    traceWeld.Job = string.Join(",", job);

                    Tools.ProduceLogTextError($"数据封装：[{traceWeld.SeamNo}],[{robot}],[{i}],[{dicOff["SeamNo"] + 1618 * i}]");

                    weldDatas.Add(traceWeld);
                }
            }
            return weldDatas;
        }

        internal static object checkLinePlc()
        {
            throw new NotImplementedException();
        }
    }
}